﻿package com.mulder.study.netty.client;

import java.net.InetSocketAddress;
import java.net.SocketAddress;

import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;

import com.mulder.study.netty.ErrorHandler;
import com.mulder.study.netty.MulderNettyBootStrap;


/**
 * Netty 를 이용하는 전형적인 아주 단순한 클라이언트 템플릿.
 * 
 * @author mulder
 */
public class MulderNettyCommonClient {
	private static final Logger logger = Logger.getLogger(MulderNettyCommonClient.class);
	
	private static final int MAX_CONNECT_SOCKET_COUNT = 3; // 접속 실패시, 재접속 최대 횟
	// default connect timeout ms
        private static final long SOCKET_CONNECT_TIMEOUT = 10 * 1000;
        
	
	public boolean running = false;
	public boolean connected = false;

	private SocketAddress socketAddress;
	private ChannelHandler channelHandler;
	private ErrorHandler errorHandler;
	
	private Thread thread;
	
	
	/**
	 * 생성자 
	 * 
	 */
	public MulderNettyCommonClient(String remoteIp, int remotePort
			, ChannelHandler channelHandler, ErrorHandler errorHandler, boolean runThread) 
	{
		super();
		this.socketAddress = new InetSocketAddress( remoteIp, remotePort);
		this.channelHandler = channelHandler;
		this.errorHandler = errorHandler;
		
		if(runThread)
		{
		    thread = new Thread(new Runnable () 
		    {
                        @Override
                        public void run()
                        {
                           execute();
                        }
		        
		    });
	            thread.start();    
		}
		
		logger.info("MulderNettyCommonClient target => "+this.socketAddress.toString());
	}
	

	/**
	 * 쓰레드 메인.
	 * 
	 */
	public void execute() 
	{
		this.running = true;
		logger.info("MulderNettyCommonClient Execute Enter !!!");

		MulderNettyBootStrap bootStrap = new MulderNettyBootStrap(false);	

		bootStrap.getBootStrap().setOption("connectTimeoutMillis", SOCKET_CONNECT_TIMEOUT);
		try {
			bootStrap.getBootStrap().setPipelineFactory(new ChannelPipelineFactory() {

				public ChannelPipeline getPipeline() throws Exception {
					return Channels.pipeline(channelHandler);
				}
			});
			
			for(int i=0 ; i<MAX_CONNECT_SOCKET_COUNT && !connected; i++)
			{
				try {
					logger.info("Connecting ["+(i+1)+"]'th.... " + this.socketAddress.toString());
					ChannelFuture future = ((ClientBootstrap)bootStrap.getBootStrap()).connect(this.socketAddress);

					future.awaitUninterruptibly();

					if (future.isSuccess())
					{
						connected = true;
						logger.info( "connect ["+(i+1)+"]'th Success !!!");
						future.getChannel().getCloseFuture().awaitUninterruptibly();
						logger.info("client bootstrap exit thread");
					} 
					else 
					{
						Thread.sleep(1000);
						logger.warn( "connect Fail ["+(i+1)+"]'th !!!");
					}	
				}
				catch (Exception ex)
				{
					ex.printStackTrace();
					logger.warn("connect Error ["+(i+1)+"]'th:" + ex.getMessage());
					
				}
			}
			
			if(!connected)
			{
				errorHandler.handleError(-1, "connect Error");
				logger.warn("Connect Fail: " + this.socketAddress.toString());
			}
			
		} 
		finally
		{
			this.running = false;
			bootStrap.releaseExternalResources();
			
			logger.info( "NettyCommonBootStrap Execute Exit !!!");
		}
	}
	
	
}